home *** CD-ROM | disk | FTP | other *** search
- /*
- * Demux.cpp
- *
- * Copyright (C) Alberto Vigata - January 2000 - ultraflask@yahoo.com
- *
- * This file is part of FlasKMPEG, a free MPEG to MPEG/AVI converter
- *
- * FlasKMPEG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * FlasKMPEG is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Make; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-
- #include "demux.h"
- #include "assert.h"
-
- #include "..\flaskmpeg.h"
-
-
-
-
-
-
-
-
-
-
- CDemux::CDemux()
- {
- inp=NULL;
- myInputStream=NULL;
- }
-
-
-
- CDemux::~CDemux()
- {
- if(myInputStream)
- delete myInputStream;
- }
-
- int CDemux::SetInput(char *inputfile)
- {
- if(inp)
- return 0; //A file was previously set. Can't set another one
- myInputStream=inp=new CinputStream;
- if(inp) return inp->OpenStream(inputfile, DVD_MODE);
- else return 0;
-
- }
- int CDemux::SetInput(CinputStream *inpStream)
- {
- if(inp)
- return 0; //A file was previously set. Can't set another one
-
- if(inpStream){
- inp=inpStream;
- return 1;
- }
- else
- return 0;
- }
- int CDemux::UnSetInput()
- {
- if(myInputStream){
- delete myInputStream;
- inp=myInputStream=NULL;
- }
- else{
- inp=NULL;
- }
- return 1;
- }
-
-
-
- i64 inline CDemux::getSCR()
- {
- if(isMPEG2)
- return ((SCRbase*300)+SCRext);
- else
- return SCR*300;
- }
-
- i64 inline CDemux::getPTS()
- {
- return PTS*300;
- }
-
-
- inline CDemux::ResetPTS()
- {
- PTS=0;
- }
- inline CDemux::ResetSCR()
- {
- SCR=0;
- }
-
-
- int CDemux::AlignPack(){
- int val=1;
- while( (inbuf[0]!=0x00 || inbuf[1]!=0x00 || inbuf[2]!=0x01 || inbuf[3]!=0xBA) && val){
- inbuf[0]=inbuf[1];
- inbuf[1]=inbuf[2];
- inbuf[2]=inbuf[3];
- val=inp->Read(&inbuf[3],1);
- }
- //Align
- if(val)
- inp->SetStreamPos(inp->GetStreamPos()-4);
- return val;
- }
-
-
- int inline CDemux::ReadPES(unsigned char **buffer, PESinfo *PES)
- {
- ui8 stream_id,substream_id;
- ui16 packet_lenght,header_lenght,bytes_read;
- ui8 ret;
-
- static ui32 pack_bytes;
-
- //Read prefix
- PTS = 0;
- PES->pack_header_parsed = false;
- start:
- PES->payloadSize = 0xFF;
-
-
- if(!( ret=inp->Read(inbuf, 3) )){
- PES->payloadSize=0xFF;
- return 0;
- }
- // 3 bytes were read succesfully
- pack_bytes += 3;
- stream_id=0;
- substream_id=0;
- if(inbuf[0]!=0 || inbuf[1]!=0 || inbuf[2]!=1){
- AlignPack(); //Stream is not pack aligned
- goto start;
- }
-
- pack_bytes += inp->Read(&stream_id, 1); //Read packet identifier
-
- if(stream_id==(ui8)PACK_ID){ //We've got a PACK
-
- PES->pack_header_parsed = true;
- //Retrieve SCR and muxrate
- //Identify either MPEG1 or MPEG2 stream
-
- pack_bytes += inp->Read(inbuf, 1);
- if( (inbuf[0] & 0xC0)==0x40 ){ //MPEG2 program stream '01'
- isMPEG2=true;
- inp->Read(&inbuf[1], 9); //Read pack header
-
- SCRbase = GET_SCRBASE(inbuf);
- SCRext = GET_SCREXT(inbuf);
- muxRate = GET_MPEG2MUXRATE(inbuf);
- //number of bytes after the byte
- //containing last bit of SCRBASE field
- pack_bytes = 5;
-
- //parse stuffing bytes
- pack_bytes += inp->Read(inbuf, GET_MPEG2STUFFING(inbuf));
- //printf("Pack parsed. \n");
- }
- else if ((inbuf[0] & 0xF0)==0x20 ){
- isMPEG2=false;
-
- inp->Read(&inbuf[1], 7); //Read pack header
-
- SCR = GET_SCR(inbuf);
- muxRate = GET_MPEG1_MUXRATE(inbuf);
- pack_bytes = 3;
- //printf("Pack parsed. \n");
- }
- else{ //arghh, there's something wrong with this stream
- AlignPack();
- goto start;
- }
- goto start; //Parse next PES or whatever
- }
-
- if( stream_id >= 0xC0 && stream_id <= 0xEF || stream_id==PRIVATE_STREAM_1){ //MPEG AUDIO or MPEG video or PRIVATE stream
- if(isMPEG2){
- pack_bytes += inp->Read(inbuf, 5);
- packet_lenght = GET_MPEG_PACKET_LENGHT(inbuf);
- header_lenght= inbuf[4];
- if(GET_MPEG2_PTS_FLAGS(inbuf)&0x02){ //if a PTS stamp is present in this PES
- pack_bytes += inp->Read(inbuf, 5);
- PTS=GET_MPEG2_PTS(inbuf);
- //Read the whole PES
- pack_bytes += inp->Read(inbuf, packet_lenght-8); //PES_packet_lenght - 'parsed data'
- *buffer= (unsigned char *)&inbuf[header_lenght - 5] ; //Set payload base
- }
- else{
- PTS=0;
- pack_bytes += inp->Read(inbuf, packet_lenght-3); //PES_packet_lenght - 'parsed data'
- *buffer= (unsigned char *)&inbuf[header_lenght]; //Set payload base
- }
- PES->payloadSize= packet_lenght - 3 - header_lenght;
-
- if(stream_id==PRIVATE_STREAM_1){
- substream_id=**buffer;
- (*buffer)+=1; //AC3 ident bytes
- PES->payloadSize-=1;
- if(substream_id >= 0x20 && substream_id < 0x40) {
- /* subpic; don't do anything special. the subpic code needs
- * everything else */
- } else {
- /* AC3 (or something else which we don't care about): skip an
- * extra 3 bytes */
- (*buffer)+=3; //AC3 ident bytes
- PES->payloadSize-=3;
- }
- }
- else
- substream_id=0;
-
- }
- else{
- //MPEG1
- pack_bytes += inp->Read(inbuf, 2);
- packet_lenght = GET_MPEG_PACKET_LENGHT(inbuf);
- bytes_read=0;
-
- // Rule: a byte has been read when a field begins
- // Read first byte
- pack_bytes += inp->Read(inbuf, 1);
- bytes_read++;
- while(inbuf[0]&0x80){ //Parse stuffing bytes
- pack_bytes += inp->Read(inbuf, 1);
- bytes_read++;
- }
-
- if( (inbuf[0]&0xC0) ==0x40 ){
- // Finish parsing STD field
- pack_bytes += inp->Read(inbuf, 1); //STD_buffer_scale & size
- bytes_read++;
- // Read next byte
- pack_bytes += inp->Read(inbuf, 1); //STD_buffer_scale & size
- bytes_read++;
- }
-
- PTS=0;
- if( (inbuf[0]&0xF0) ==0x20 ){ //PTS stamp present
- pack_bytes += inp->Read(&inbuf[1], 4);
- bytes_read+=4;
- PTS=GET_MPEG1_PTS(inbuf);
- }
- else if( (inbuf[0]&0xF0) ==0x30 ){ //PTS & DTS stamp present
- pack_bytes += inp->Read(&inbuf[1], 9);
- bytes_read+=9;
- PTS=GET_MPEG1_PTS(inbuf);
- }
- // else '00001111' 0x0F
- else{
- PTS=0;
- }
- PES->payloadSize= packet_lenght - bytes_read;
-
- pack_bytes += inp->Read(inbuf, PES->payloadSize);
- *buffer= (unsigned char *)&inbuf[0];
- }
- }
- else{
- //Fetch PES length
- pack_bytes += inp->Read(inbuf, 2);
- pack_bytes += inp->Read(inbuf, GET_UINT16(inbuf));
- }
-
- PES->muxrate = muxRate;
- PES->PTS = getPTS();
- PES->SCR = getSCR();
- PES->streamID = stream_id;
- PES->subStreamID = substream_id;
- PES->pack_bytes = pack_bytes;
- return ret;
- }
-
-
- i64 CDemux::GetTime()
- {
- return getSCR();
- }
-
- int CDemux::ReadLPES(unsigned char **buffer, PESinfo *pInfo)
- {
- ui32 ret;
- i64 nextSCR;
-
- ret=ReadPES(buffer, pInfo);
- if(!ret)
- return ret;
-
- if(firstTime){
- delta = 0 - pInfo->SCR;
- firstTime = false;
- }
- else{
- // Handle clock disruption
- if((lastSCR >= pInfo->SCR) &&
- pInfo->pack_header_parsed )
- {
- // work out the time of the last byte of the previous pack
- nextSCR = lastSCR +
- (int)(( (double)(lastPackBytes + 1)
- / (double)(lastMuxRate *50 ) )
- *(double)MPEG2_CLK_REF);
-
- // update delta
- // delta = OUR_CLK - STREAM_CLK
- // OUR_CLK = STREAM_CLK + delta
- delta = (nextSCR + delta) - pInfo->SCR;
- }
- }
- lastSCR = pInfo->SCR;
- lastPackBytes = pInfo->pack_bytes;
- lastMuxRate = pInfo->muxrate;
-
- pInfo->SCR = pInfo->SCR + delta;
- pInfo->PTS = pInfo->PTS ? (pInfo->PTS + delta) : 0;
-
-
-
- return ret;
- }
-
- void CDemux::StartReadLPES()
- {
- // ReadLPES variables
- delta = 0;
- lastSCR = 0;
- lastPackBytes = 0;
- lastMuxRate = 0;
- firstTime = true;
- return;
- }
-
- int CDemux::SetWorkingMode(int mode)
- {
- return inp->SetWorkingMode(mode);
- }
-